package com.falser.user.service.impl;

import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.falser.common.dto.MenuDTO;
import com.falser.common.dto.UserInfoDTO;
import com.falser.common.enums.DeleteEnum;
import com.falser.common.enums.PermissionTypeEnum;
import com.falser.common.enums.RedisKeyEnum;
import com.falser.common.enums.Status;
import com.falser.common.exception.BaseException;
import com.falser.user.dao.SysUserDao;
import com.falser.user.dto.LoginSuccessDTO;
import com.falser.user.dto.UserDetailDTO;
import com.falser.user.entity.SysPermission;
import com.falser.user.entity.SysRole;
import com.falser.user.entity.SysUser;
import com.falser.user.entity.SysUserRole;
import com.falser.user.enums.StatusEnum;
import com.falser.user.enums.UserTypeEnum;
import com.falser.user.service.SysRolePermissionService;
import com.falser.user.service.SysUserRoleService;
import com.falser.user.service.SysUserService;
import com.falser.user.vo.LoginVO;
import com.falser.user.vo.RegisterVO;
import com.falser.user.vo.SysUserInsertOrUpdateVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 用户信息表(SysUser)表服务实现类
 *
 * @author makejava
 * @since 2021-09-16 20:49:57
 */
@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUser> implements SysUserService {

    private final PasswordEncoder passwordEncoder;

    private final SysRolePermissionService sysRolePermissionService;

    private final SysUserRoleService sysUserRoleService;


    public SysUserServiceImpl(PasswordEncoder passwordEncoder,
                              SysRolePermissionService sysRolePermissionService,
                              SysUserRoleService sysUserRoleService) {
        this.passwordEncoder = passwordEncoder;
        this.sysRolePermissionService = sysRolePermissionService;
        this.sysUserRoleService = sysUserRoleService;
    }

    @Override
    public void register(RegisterVO vo) {
        SysUser one = getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getMobile, vo.getMobile()));
        if (Objects.nonNull(one)) {
            throw new BaseException("手机号已注册！");
        }

        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(vo, sysUser);
        sysUser.setCreateTime(LocalDateTime.now());
        sysUser.setPassword(passwordEncoder.encode(vo.getPassword()));
        sysUser.setDelFlag(DeleteEnum.UN_DELETED.getCode());
        sysUser.setPwdUpdateDate(LocalDateTime.now());
        sysUser.setUserType(UserTypeEnum.BLOG_USER);
        save(sysUser);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSystemUser(SysUserInsertOrUpdateVO vo) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(vo, sysUser);
        sysUser.setSex(vo.getSex());
        sysUser.setUserType(vo.getUserType());
        sysUser.setStatus(vo.getStatus().name());
        checkAlreadyExist(sysUser);
        sysUser.setPassword(passwordEncoder.encode("123456"));
        // 修改
        if (Objects.nonNull(sysUser.getId())) {
            updateById(sysUser);
        } else {
            save(sysUser);
        }
    }

    @Override
    public LoginSuccessDTO login(LoginVO vo) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getLoginName, vo.getLoginName())
                .eq(SysUser::getStatus, StatusEnum.ENABLE.name());
        SysUser one = getOne(queryWrapper);
        if (Objects.nonNull(one)) {
            if (passwordEncoder.matches(vo.getPassword(), one.getPassword())) {
                UUID uuid = UUID.randomUUID();
                StpUtil.login(uuid, new SaLoginModel().setTimeout(60 * 60 * 24 * 7));
                // 用户角色
                Long userId = one.getId();
                saveRolePermissionToRedis(userId);
                StpUtil.getSession().set("userinfo", getUserInfo(userId));
                one.setLoginIp(StpUtil.getLoginDevice());
                one.setLoginDate(LocalDateTime.now());
                updateById(one);
                return new LoginSuccessDTO()
                        .setToken(StpUtil.getTokenValue())
                        .setTokenKey(StpUtil.getTokenName());
            }
        }
        throw new BaseException(Status.USERNAME_PASSWORD_ERROR);
    }

    private UserInfoDTO getUserInfo(Long userId) {
        SysUser sysUser = getById(userId);
        UserInfoDTO target = new UserInfoDTO();
        target.setName(sysUser.getUserName());
        target.setAvatar(sysUser.getAvatar());
        List<SysRole> sysRoleList = sysUserRoleService.getRoleListByUserId(userId);
        List<Long> idList = sysRoleList.stream().map(SysRole::getId).collect(Collectors.toList());
        List<String> roles = sysRoleList.stream().map(role-> role.getRoleKey().name()).collect(Collectors.toList());
        target.setRoles(roles);
        List<SysPermission> permissionListByRoleIds = sysRolePermissionService.getPermissionListByRoleIds(idList);
        List<MenuDTO> menus = permissionListByRoleIds
                .stream()
                .filter(sysPermission -> Objects.equals(PermissionTypeEnum.MENU.name(), sysPermission.getPermissionType()))
                .map(sysPermission -> {
                    MenuDTO menuDTO = new MenuDTO();
                    BeanUtils.copyProperties(sysPermission, menuDTO);
                    return menuDTO;
                })
                .collect(Collectors.toList());
        target.setMenus(menus);
        return target;
    }

    /**
     * 保存角色和权限到redis
     *
     * @param userId 用户id
     */
    private void saveRolePermissionToRedis(Long userId) {
        List<SysRole> roleList = sysUserRoleService.getRoleListByUserId(userId);
        String tokenValue = StpUtil.getTokenValue();
        StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.USER_ID_TOKEN_REF_KEY.getKey() + tokenValue, userId);
        // 用户权限
        if (!CollectionUtils.isEmpty(roleList)) {
            StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.LOGIN_ROLES_KEY.getKey() + tokenValue, roleList
                    .stream()
                    .map(SysRole::getRoleKey)
                    .collect(Collectors.toList()));

            List<Long> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toList());

            List<SysPermission> permissionListByRoleIds = sysRolePermissionService.getPermissionListByRoleIds(roleIds);

            if (!CollectionUtils.isEmpty(permissionListByRoleIds)) {
                StpUtil.getSession().set(RedisKeyEnum.LoginKeyEnum.LOGIN_PERMISSIONS_KEY.getKey() + tokenValue, permissionListByRoleIds
                        .stream()
                        .map(sysPermission -> sysPermission.getMethod() + "#" + sysPermission.getUrl())
                        .collect(Collectors.toList()));
            }

        }
    }

    @Override
    public void logout() {
        StpUtil.logout();
    }

    @Override
    public UserInfoDTO getUserInfo() {
        return (UserInfoDTO) StpUtil.getSession().get("userinfo");
    }

    @Override
    public UserDetailDTO getUserDetailById(Long id) {
        return baseMapper.getUserDetailById(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteUserById(Long id) {
        removeById(id);
        sysUserRoleService.remove(new LambdaQueryWrapper<SysUserRole>().eq(SysUserRole::getUserId, id));
    }

    /**
     * 检查已经存在
     *
     * @param sysUser 系统用户
     */
    private void checkAlreadyExist(SysUser sysUser) {
        SysUser user = getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getMobile, sysUser.getMobile()));
        if (Objects.nonNull(user)) {
            throw new BaseException(Status.PHONE_EXIST_EXCEPTION);
        }

        SysUser user1 = getOne(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getLoginName, sysUser.getLoginName()));
        if (Objects.nonNull(user1)) {
            throw new BaseException(Status.USER_EXIST_EXCEPTION);
        }

    }
}

